# coding: utf-8
from pathlib import Path
from typing import Literal, Union, List

from playwright.sync_api import Page, Locator, FrameLocator, BrowserContext


class PlayWrightUtil:
    """
    Playwright utility class.
    """

    def __init__(self, page: Page):
        self._page = page
        self._context = self._page.context

    def page(self) -> Page:
        """
        获取当前页面对象。
        Returns the current page object.
        :return:
        """
        return self._page

    def context(self) -> BrowserContext:
        """
        获取当前上下文对象。
        Returns the current context object.
        :return:
        """
        return self._context

    def title(self):
        """
        获取页面标题
        :return:
        """
        return self._page.title()

    def reload(self):
        """
        刷新页面
        :return:
        """
        self._page.reload()

    def go_back(self):
        """
        后退页面
        :return:
        """
        self._page.go_back()

    def go_forward(self):
        """
        前进页面
        :return:
        """
        self._page.go_forward()

    def open(self, url: str):
        """
        在浏览器中打开给定的URL。
        Opens the given URL in the browser.
        """
        self._page.goto(url)

    def set_viewport_size(self, width: int, height: int):
        """
        设置视窗大小
        :return:
        """
        self._page.set_viewport_size({"width": width, "height": height})

    def switch_to_page(self, index: Union[str, int]):
        """
        切换到指定页面
        :param index:
        :return:
        """
        page = self._page.context.pages[index]
        page.bring_to_front()
        self._page = page

    def locator(self, selector: str) -> Locator:
        """
        获取元素定位器
        :param selector:
        :return:
        """
        return self._page.locator(selector)

    def frame_locator(self, selector: str) -> FrameLocator:
        """
        获取 frame / iframe 定位器
        :param selector:
        :return:
        """
        return self._page.frame_locator(selector)

    def click(self, selector: str):
        """
        点击
        :param selector:
        :return:
        """
        self.locator(selector).click()

    def uncheck(self, selector: str):
        """
        取消选中
        :param selector:
        :return:
        """
        self.locator(selector).uncheck()

    def is_checked(self, selector: str):
        """
        判断是否选中
        :param selector:
        :return:
        """
        return self.locator(selector).is_checked()

    def check(self, selector: str):
        """
        选中
        :param selector:
        :return:
        """
        self.locator(selector).check()

    def input(self, selector: str, value: str):
        """
        在指定的元素中输入数据
        :param selector:
        :param value:
        :return:
        """
        self.locator(selector).fill(value)

    def input_files(self, selector: str, files: Union[List[Union[Path, str]], Union[Path, str]]):
        """
        设置文件上传
        :param selector:
        :param files:
        :return:
        """
        self.locator(selector).set_input_files(files)

    def hover(self, selector: str):
        """
        悬停
        :param selector:
        :return:
        """
        self._page.locator(selector).hover()

    def focus(self, selector: str):
        """
        聚焦
        :param selector:
        :return:
        """
        self._page.locator(selector).focus()

    def type(self, selector: str, value: str):
        """
        输入
        :param selector:
        :param value:
        :return:
        """
        self.locator(selector).type(value)

    def wait_for(
            self,
            selector: str,
            state: Literal["attached", "detached", "visible", "hidden", "stable"] = 'visible',
            timeout: int = 30000
    ):
        """
        等待页面导航
        :param selector:
        :return:
        """
        self.locator(selector).wait_for(state, timeout=timeout)

    def wait_for_load_state(
            self,
            state: Literal["domcontentloaded", "load", "networkidle"] = 'load',
            timeout: int = 30000
    ):
        """
        等待页面加载状态
        :param state:
        :param timeout:
        :return:
        """
        self._page.wait_for_load_state(state=state, timeout=timeout)

    def wait_for_timeout(self, timeout: int):
        """
        等待指定时间
        :param timeout:
        :return:
        """
        self._page.wait_for_timeout(timeout)

    def wait_for_selector(self, selector: str, timeout: int = 30000):
        """
        等待元素出现
        :param selector:
        :param timeout:
        :return:
        """
        self._page.wait_for_selector(selector, timeout=timeout)

    def screenshot(self, path: str = None, **kwargs) -> bytes:
        """
        截图
        :param path: 截图保存路径
        :param kwargs:
        :return: 截图对象 bytes
        """
        return self._page.screenshot(path=path, **kwargs)

    def inner_text(self, selector: str):
        """
        获取元素内文本
        :param selector:
        :return:
        """
        return self.locator(selector).inner_text()

    def all_inner_texts(self, selector: str):
        """
        获取所有元素内文本
        :param selector:
        :return:
        """
        return self.locator(selector).all_inner_texts()

    def inner_html(self, selector: str):
        """
        获取元素内html
        :param selector:
        :return:
        """
        return self.locator(selector).inner_html()

    def text_content(self, selector: str):
        """
        获取元素文本内容
        :param selector:
        :return:
        """
        return self.locator(selector).text_content()

    def all_text_contents(self, selector: str):
        """
        获取所有元素文本内容
        :param selector:
        :return:
        """
        return self.locator(selector).all_text_contents()

    def get_attribute(self, selector: str, attribute: str):
        """
        获取元素属性
        :param selector: 元素选择器
        :param attribute: 属性名
        :return:
        """
        return self.locator(selector).get_attribute(attribute)

    def is_visible(self, selector: str):
        """
        判断元素是否可见
        :param selector:
        :return:
        """
        return self.locator(selector).is_visible()

    def select_option(self, selector: str, value: str):
        """
        选择下拉框选项
        :param selector:
        :param value:
        :return:
        """
        self.locator(selector).select_option(value)

    def select_option_by_index(self, selector: str, index: int):
        """
        选择下拉框选项
        :param selector:
        :param index:
        :return:
        """
        self.locator(selector).select_option(index=index)

    def select_option_by_label(self, selector: str, label: str):
        """
        选择下拉框选项
        :param selector:
        :param label:
        :return:
        """
        self.locator(selector).select_option(label=label)

    def select_option_clear(self, selector: str):
        """
        清空下拉框选项
        :param selector:
        :return:
        """
        self.locator(selector).select_option([])

    def drag_to(self, selector: str, target: str):
        """
        拖拽元素到目标元素
        :param selector:
        :param target:
        :return:
        """
        self.locator(selector).drag_to(self.locator(target))

    def drag_and_drop(self, source: str, target: str):
        """
        拖拽元素到目标元素
        :param source:
        :param target:
        :return:
        """
        self._page.drag_and_drop(source, target)

    def evaluate(self, expression: str):
        """
        执行js表达式, 返回 `expression` 调用的值。
        :param expression:
        :return:
        """
        return self._page.evaluate(expression)

    def evaluate_handle(self, expression: str):
        """
        执行js表达式, 返回 `expression` 调用的值作为 `JSHandle`。
        :param expression:
        :return:
        """
        return self._page.evaluate_handle(expression)

    def python_eval(self, expression: str):
        """
        执行python表达式, 返回 `expression` 调用的值。
        :param expression:
        :return:
        """
        return eval(expression)

    def python_exec(self, expression: str):
        """
        执行python表达式, 返回 `expression` 调用的值。
        :param expression:
        :return:
        """
        return exec(expression)

    def close(self):
        """
        关闭浏览器
        :return:
        """
        self._page.close()

    def close_context(self):
        """
        关闭浏览器上下文
        :return:
        """
        self._page.context.close()

    def __dir__(self):
        return ['page', 'title', 'reload', 'go_back', 'go_forward', 'open', 'set_viewport_size', 'locator',
                'frame_locator', 'click', 'uncheck', 'is_checked', 'check', 'input', 'input_files', 'hover', 'focus',
                'type', 'wait_for', 'wait_for_load_state', 'wait_for_timeout', 'screenshot', 'wait_for_selector',
                'inner_text', 'all_inner_texts', 'inner_html', 'text_content', 'all_text_contents', 'get_attribute',
                'is_visible', 'select_option', 'select_option_by_index', 'select_option_by_label',
                'select_option_clear', 'drag_to', 'drag_and_drop', 'evaluate', 'evaluate_handle', 'python_eval',
                'python_exec', 'close', 'close_context', 'switch_to_page']
